<?php
/**
 * Created by PhpStorm.
 * User: LiuJiaJun
 * Date: 2019/1/12
 * Time: 0:22
 */

namespace api\storage\service\inventory;

use api\storage\common\Constant;
use api\storage\model\BomDetailModel;
use api\storage\model\BomModel;
use api\storage\model\OutflowModel;
use api\storage\model\ReelsModel;
use api\storage\model\SeatModel;
use api\storage\model\StorageModel;
use api\storage\service\BaseService;
use api\storage\service\BomService;
use api\storage\service\OutflowService;
use api\storage\service\StorageService;
use service\file\CommonFile;
use service\file\FileLock;
use service\logger\LoggerFactory;

/**
 * 盘点基类
 *
 * Class BaseInventoryService
 * @package api\storage\service\inventory
 */
abstract class BaseInventoryService extends BaseService
{
    protected $rootPath;
    protected $path;
    protected $logger;

    public function __construct()
    {
        $this->rootPath = Constant::RECORD_INVENTORY;
        $this->logger = LoggerFactory::getLogger('inventory');
    }

    /**
     * 参数检查
     *
     * @param $code
     * @param $storageUuid
     * @return array|mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function checkParam($code, $storageUuid)
    {
        $storageModel = new StorageModel();
        $storage = $storageModel->findStorageByUuid($storageUuid);
        if (!empty($storage) && !empty($storage->getData('bom_uuid'))) {
            return $this->resultTemplate(-3);
        }
        return $this->afterCheck($code, $storageUuid);
    }

    /**
     * 获取盘点数据
     *
     * @param $code
     * @param $storageUuid
     * @return array
     */
    public function getReelsData($code, $storageUuid)
    {
        $lock = new FileLock($storageUuid);
        $lock->lock(FileLock::LOCK_TYPE_INVENTORY);
        $exist = $this->initInventoryFile($code, $storageUuid);
        $file = new CommonFile($storageUuid, $this->path);
        if ($exist) {
            $content = $this->jsonDecode($file->read());
            $content['person'] += 1;
            $file->write($content);
            $lock->unlock();
        } else {
            $result = $this->getReels($code, $storageUuid);
            $content = $this->handleReels($result);
            $file->write($this->inventoryTemplate($content['reels'], $result['seatId']));
            $lock->unlock();
        }
        //没有数据的话，直接取消盘点了
        if (empty($content['reels'])) {
            $file->delete();
            $this->remakeFlag($code, $storageUuid, false);
            return $this->resultTemplate(0);
        }
        return $this->resultTemplate(1, $content['reels']);
    }

    /**
     * 检查物料是否在盘点文件中
     *
     * @param $code
     * @param $reelId
     * @param $storageUuid
     * @return array
     */
    public function checkReels($code, $reelId, $storageUuid)
    {
        $exits = $this->initInventoryFile($code, $storageUuid);
        if (empty($exits)) {
            return $this->resultTemplate(-2);
        }
        $file = new CommonFile($storageUuid, $this->path);
        $content = $this->jsonDecode($file->read());
        $reels = array_column($content['reels'], 'reelId');
        $index = array_search($reelId, $reels);
        if ($index !== false) {
            return $this->resultTemplate(1, ['value' => $content['reels'][$index]]);
        } else {
            return $this->resultTemplate(-1);
        }
    }

    /**
     * 检查站位是否在盘点中
     *
     * @param $code
     * @param $seatCode
     * @param $storageUuid
     * @return array
     */
    public function checkSeat($code, $seatCode, $storageUuid)
    {
        $exits = $this->initInventoryFile($code, $storageUuid);
        if (empty($exits)) {
            return $this->resultTemplate(-2);
        }
        $file = new CommonFile($storageUuid, $this->path);
        $content = $this->jsonDecode($file->read());
        $reels = array_column($content['reels'], 'seatCode');
        $index = array_search($seatCode, $reels);
        if ($index !== false) {
            return $this->resultTemplate(1, ['value' => $content['reels'][$index]]);
        } else {
            return $this->resultTemplate(-1);
        }
    }

    /**
     * 确认物料的信息，有可能会进行数量的变更
     *
     * @param $code
     * @param $reelId
     * @param $updateCount
     * @param $storageUuid
     * @param $nickname
     * @return array
     */
    public function confirmReels($code, $reelId, $updateCount, $storageUuid, $nickname)
    {
        $lock = new FileLock($storageUuid);
        $lock->lock(FileLock::LOCK_TYPE_INVENTORY);
        $this->initInventoryFile($code, $storageUuid);
        $file = new CommonFile($storageUuid, $this->path);
        $content = $this->jsonDecode($file->read());
        $reelsModel = new ReelsModel();
        $res = $reelsModel->updateReel($reelId, $storageUuid, ['count' => $updateCount]);
        $this->logger->debug("用户：{$nickname}，把reelId：{$reelId}，数量更新为：{$updateCount}");
        if ($res === false) {
            $lock->unlock();
            return $this->resultTemplate(-1, $content['reels']);
        }
        //查找出reelId所在的位置
        $reels = array_column($content['reels'], 'reelId');
        $index = array_search($reelId, $reels);
        //更新操作
        $content['reels'][$index]['isFinish'] = true;
        $content['reels'][$index]['count'] = $updateCount;
        $isFinishList = array_unique(array_column($content['reels'], 'isFinish'));
        //查看全部更新是否完成
        $finish = !(in_array(false, $isFinishList));
        $content['isFinish'] = $finish;
        //后置处理
        $this->afterHandleLightOff($content['reels'][$index]['seatId'], $storageUuid);
        $file->write($content);
        $lock->unlock();
        return $this->resultTemplate(1, $content['reels']);
    }

    /**
     * 完成盘点
     *
     * @param $code
     * @param $storageUuid
     * @param $nickname
     * @return array
     * @throws \think\Exception
     * @throws \think\exception\PDOException
     */
    public function complete($code, $storageUuid, $nickname)
    {
        return $this->endInventory($code, $storageUuid, $nickname, 1);
    }

    /**
     * 取消盘点
     *
     * @param $code
     * @param $storageUuid
     * @param $nickname
     * @return array
     * @throws \think\Exception
     * @throws \think\exception\PDOException
     */
    public function cancel($code, $storageUuid, $nickname)
    {
        return $this->endInventory($code, $storageUuid, $nickname, 2);

    }

    /*
     * abstract 方法,强制子类实现具体的方法
     */

    /**
     * 抽象方法，用户子类检查参数是否正确
     *
     * @param $code
     * @param $storageUuid
     * @return mixed
     */
    abstract protected function afterCheck($code, $storageUuid);

    /**
     * 抽象方法，用于子类根据不同情况获取物料号
     *
     * @param $code
     * @param $storageUuid
     * @return mixed
     */
    abstract protected function getReels($code, $storageUuid);

    /**
     * 抽象方法，用于子类对物料号进行处理
     *
     * @param $reels
     * @return mixed
     */
    abstract protected function handleReels($reels);

    /*
     * protected 方法,需要实现具体方法的子类进行重写
     */

    /**
     *  用于子类对盘点进行设定标志
     *
     * @param $code
     * @param $storageUuid
     * @param $isOccupy
     * @return mixed
     */
    protected function remakeFlag($code, $storageUuid, $isOccupy)
    {
        return true;
    }

    /**
     * protected方法，用户子类中有需要的地方调用
     *
     * @param $seatId
     * @param $storageUuid
     */
    protected function afterHandleLightOff($seatId, $storageUuid)
    {

    }

    /**
     * 退出盘点
     *
     * @param $code
     * @param $storageUuid
     * @param $nickname
     * @param $type
     * @return array
     * @throws \think\Exception
     * @throws \think\exception\PDOException
     */
    private function endInventory($code, $storageUuid, $nickname, $type)
    {
        $lock = new FileLock($storageUuid);
        $lock->lock(FileLock::LOCK_TYPE_INVENTORY);
        $this->initInventoryFile($code, $storageUuid);
        $file = new CommonFile($storageUuid, $this->path);
        $content = $this->jsonDecode($file->read());
        $content['person']--;
        $file->write($content);
        if (empty($content['person'])) {
            $reels = [];
            foreach ($content['reels'] as $item) {
                if ($this->judgeQuitType($type, $item)) {
                    $reels[] = $item;
                }
            }
            $res = $this->handleExtraReels($reels, $storageUuid, $nickname);
            $clean = $this->remakeFlag($code, $storageUuid, false);
            $lock->unlock();
            $this->afterHandleLightOff($content['seatId'], $storageUuid);
            if (empty($res)) {
                return $this->resultTemplate(-2);
            } elseif (empty($clean)) {
                return $this->resultTemplate(-1);
            } else {
                $file->delete();
                return $this->resultTemplate(1);
            }
        }
        $lock->unlock();
        return $this->resultTemplate(1);

    }

    /**
     * 初始化出库文件
     *
     * @param $code
     * @param $storageUuid
     * @return bool
     */
    private function initInventoryFile($code, $storageUuid)
    {
        $this->path = $this->rootPath . $code . Constant::RECORD_FILE_SUFFIX;
        //文件临时目录
        $tempPath = $this->rootPath . $storageUuid . '/' . $code . Constant::RECORD_FILE_SUFFIX;
        if (file_exists(CMF_ROOT . $tempPath)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     *  处理 实物不存在的物料
     *
     * @param $reels
     * @param $storageUuid
     * @param $nickname
     * @return bool
     * @throws \think\Exception
     * @throws \think\exception\PDOException
     */
    private function handleExtraReels($reels, $storageUuid, $nickname)
    {
        if (empty($reels)) {
            return true;
        }
        $storageName = StorageService::getStorage($storageUuid)['name'];
        $orderId = create_unique_id('PD');
        $remark = $storageName . '盘点出库';
        $bom = BomService::generateBom($orderId, '盘点出库', $storageUuid, $remark);
        $outflow = $bomDetail = [];
        foreach ($reels as $item) {
            $materielId = $item['subMaterielId'];
            if (in_array($materielId, array_keys($bomDetail))) {
                $bomDetail[$materielId]['count'] += $item['outCount'];
            } else {
                $bomDetail[$materielId]['uuid'] = create_uuid();
                $bomDetail[$materielId]['bom_uuid'] = $bom['uuid'];
                $bomDetail[$materielId]['sub_materiel_id'] = $materielId;
                $bomDetail[$materielId]['count'] = $item['outCount'];
            }
            $outflow[] = OutflowService::generateOutflow($item['reelId'], $item['outCount'], $item['subMaterielId'],
                $orderId, $storageUuid, $nickname, $remark);
        }
        //做一次出库单导入并且出库操作
        $outflowRes = $detailRes = true;
        $bomModel = new BomModel();
        $bomModel->startTrans();
        $bomRes = $bomModel->add($bom);
        if (!empty($bomDetail)) {
            $bomDetailModel = new BomDetailModel();
            $detailRes = $bomDetailModel->insertAll(array_values($bomDetail));
        }
        $reelsModel = new ReelsModel();
        $reelsRes = $reelsModel->deleteByUuidList(array_column($reels, 'uuid'));
        $seatModel = new SeatModel();
        $seatRes = $seatModel->updateBatchNotUsed(array_unique(array_column($reels, 'seatCode')), $storageUuid);
        if (!empty($outflow)) {
            $outflowModel = new OutflowModel();
            $outflowRes = $outflowModel->insertAll($outflow);
        }
        if (empty($bomRes) || empty($outflowRes) || empty($detailRes) || $reelsRes === false || $seatRes === false) {
            $bomModel->rollback();
            return false;
        } else {
            $bomModel->commit();
            return true;
        }
    }

    /**
     * 判断退出的条件
     *
     * @param $type
     * @param $item
     * @return bool
     */
    private function judgeQuitType($type, $item)
    {
        switch ($type) {
            case 1:
                return $item['isFinish'] == false || $item['count'] == 0;
                break;
            case 2:
                return $item['count'] == 0;
                break;
            default:
                return false;
        }
    }

    /**
     * 盘点模本文件内容
     *
     * @param array $reels
     * @param array $seatIds
     * @param int $person
     * @param bool $isFinish
     * @return array
     */
    private function inventoryTemplate($reels = [], $seatIds = [], $person = 1, $isFinish = false)
    {
        return [
            'reels' => $reels,
            'seatId' => $seatIds,
            'person' => $person,
            'isFinish' => $isFinish,
            'time' => get_current_time()
        ];
    }
}